%%% dmap.erl - encoder/ decoder for the binary serialisation used by daap
%%%
%%% The MIT License
%%%
%%% Copyright (c) 2008 hellomatty@gmail.com
%%%
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
%%% of this software and associated documentation files (the "Software"), to deal
%%% in the Software without restriction, including without limitation the rights
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
%%% copies of the Software, and to permit persons to whom the Software is
%%% furnished to do so, subject to the following conditions:
%%%
%%% The above copyright notice and this permission notice shall be included in
%%% all copies or substantial portions of the Software.
%%%
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
%%% THE SOFTWARE.
%%%

-module(dmap).
-export([decode/1, encode/1]).

-import(content_codes).
-include_lib("util.hrl").

decode(Bytes) ->
  decode([], Bytes).

decode(Tags, <<>>) ->
  lists:reverse(Tags);

decode(Tags, Bytes) when is_binary(Bytes) ->
  {Tag, Rest} = decode_tag(Bytes),
  decode([Tag|Tags], Rest).

decode_tag(Bytes) when is_binary(Bytes) ->
  {Header,Rest} = split_binary(Bytes, 8),
  {Tag, <<Size:32>>} = split_binary(Header, 4),
  DecodedTag = list_to_atom(binary_to_list(Tag)),
  {ValueBytes, Rest2} = split_binary(Rest, Size),
  {{DecodedTag, decode_value(content_codes:type(DecodedTag), ValueBytes)}, Rest2}.



decode_value(int, <<Value:32>>) ->
  Value;
decode_value(list, ChildBytes) ->
  decode_value(list, ChildBytes, []);
decode_value(long, <<Value:64>>) ->
  Value;
decode_value(string, Bytes) ->
  asn1rt:utf8_binary_to_list(Bytes);
decode_value(short, <<Value:16>>) ->
  Value;
decode_value(date, <<Value:32>>) ->
  Value;
decode_value(byte, <<0>>) ->
  false;
decode_value(byte, <<1>>) ->
  true;
decode_value(byte, <<Value:8>>) ->
  Value.

decode_value(list, <<>>, ChildTags) ->
  lists:reverse(ChildTags);
decode_value(list, Bytes, ChildTags) ->
  {Tag, Rest} = decode_tag(Bytes),
  decode_value(list, Rest, [Tag|ChildTags]).



encode({Tag, Children}) when is_list(Children) ->
  case content_codes:type(Tag) of
    string ->
      {ok, Bin} = asn1rt:utf8_list_to_binary(Children),
      encode({Tag, Bin});
    _ -> 
      Payload = list_to_binary([encode(C) || C <- Children]),
      encode({Tag, Payload})
  end;
encode({Tag, Value}) when is_integer(Value) ->
  case content_codes:type(Tag) of
    byte ->
      encode({Tag, <<Value:8>>});
    short ->
      encode({Tag, <<Value:16>>});
    int -> 
      encode({Tag, <<Value:32>>});
    date -> 
      encode({Tag, <<Value:32>>});
    long ->
      encode({Tag, <<Value:64>>})
    end;
encode({Tag, Tag2}) when is_atom(Tag2) ->
  encode({Tag, list_to_binary(atom_to_list(Tag2))});
encode({Tag, {version, Major, Minor}}) ->
  encode({Tag, <<Major:16, Minor:16>>});
encode({Tag, true}) ->
  encode({Tag, <<1:8>>});
encode({Tag, false}) ->
  encode({Tag, <<0:8>>});
encode({Tag, Data}) when is_binary(Data) ->
  list_to_binary([atom_to_list(Tag), <<(size(Data)):32>>, Data]).
